package com.hgsoft.pubsv.component.apifilter.util;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.tomcat.util.codec.binary.Base64;

import com.hgsoft.pubsv.component.base.Constants;
import com.hgsoft.pubsv.component.base.execption.ApiCodeException;

import lombok.extern.slf4j.Slf4j;

/**
 * @Description: RSA加解密工具类
 */
@Slf4j
public class RSAUtils {

	/**
	 * 算法标识
	 */
	private static final String ALGORITHM = "RSA";
	/**
	 * 密钥长度
	 */
	private static final int KEY_SIZE = 1024;
	/**
	 * 编码格式
	 */
	private static final String CHARSET_NAME = "UTF-8";

	/**
	 * 最大加密字节数117，超出最大字节数需要分组加密
	 */
	private static int MAX_ENCRYPT_BLOCK = 117;

	/**
	 * 最大解密字节数128，超出最大字节数需要分组加密
	 */
	private static int MAX_DECRYPT_BLOCK = 128;

	/**
	 * 随机生成密钥对
	 *
	 * @throws Exception
	 */
	public static Map<Integer, String> generateKeyPair() throws Exception {
		Map<Integer, String> keyPairMap = new HashMap<Integer, String>();
		// KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
		// 初始化密钥对生成器，密钥大小为96-1024位
		keyPairGen.initialize(KEY_SIZE, new SecureRandom());
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
		String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
		keyPairMap.put(0, publicKeyString);
		keyPairMap.put(1, privateKeyString);
		return keyPairMap;
	}

	/**
	 * RSA公钥加密
	 *
	 * @param str
	 *            待加密字符串
	 * @param publicKey
	 *            公钥
	 * @return 密文字符串
	 * @throws Exception
	 *             加密过程中出现的异常
	 */
	public static String encrypt(String str, String publicKey) {
		try {
			// base64编码的公钥
			byte[] decoded = Base64.decodeBase64(publicKey);
			RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(ALGORITHM)
					.generatePublic(new X509EncodedKeySpec(decoded));
			// RSA加密
			Cipher cipher = Cipher.getInstance(ALGORITHM);
			cipher.init(Cipher.ENCRYPT_MODE, pubKey);
			String outStr = null;
			byte[] inputArray = str.getBytes(CHARSET_NAME);
			int inputLength = inputArray.length;
			// 最大加密字节数，超出最大字节数需要分组加密
			int offSet = 0; // 标识
			byte[] resultBytes = {};
			byte[] cache = {};
			while (inputLength - offSet > 0) {
				if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
					cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
					offSet += MAX_ENCRYPT_BLOCK;
				} else {
					cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
					offSet = inputLength;
				}
				resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
				System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
			}
			outStr = Base64.encodeBase64String(resultBytes);
			return outStr;
		} catch (Exception e) {
			log.error("RSA 加密异常，明文:{},公钥:{}, e:{}", str, publicKey, e);
			throw new ApiCodeException(Constants.ERR_ENCRYPTION_FAILED,Constants.ERR_ENCRYPTION_FAILED_NAME);
		}
	}

	/**
	 * RSA私钥解密
	 *
	 * @param str
	 *            加密字符串
	 * @param privateKey
	 *            私钥
	 * @return 明文字符串
	 * @throws Exception
	 *             解密过程中出现的异常
	 */
	public static String decrypt(String str, String privateKey) {
		try {
			byte[] decoded = Base64.decodeBase64(privateKey);
			RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(ALGORITHM)
					.generatePrivate(new PKCS8EncodedKeySpec(decoded));
			Cipher cipher = Cipher.getInstance(ALGORITHM);
			cipher.init(Cipher.DECRYPT_MODE, priKey);
			String outStr = null;
			byte[] inputArray = Base64.decodeBase64(str.getBytes(CHARSET_NAME));
			int inputLength = inputArray.length;
			// 最大加密字节数，超出最大字节数需要分组加密
			int offSet = 0; // 标识
			byte[] resultBytes = {};
			byte[] cache = {};
			while (inputLength - offSet > 0) {
				if (inputLength - offSet > MAX_DECRYPT_BLOCK) {
					cache = cipher.doFinal(inputArray, offSet, MAX_DECRYPT_BLOCK);
					offSet += MAX_DECRYPT_BLOCK;
				} else {
					cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
					offSet = inputLength;
				}
				resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
				System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
			}
			outStr = new String(resultBytes, CHARSET_NAME);
			return outStr;
		} catch (Exception e) {
			log.error("RSA 解密异常，密文:{},私钥:{}, e:{}", str, privateKey, e);
			throw new ApiCodeException(Constants.ERR_DECRYPT_FAILED,Constants.ERR_DECRYPT_FAILED_NAME);
		}
	}

}
